﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace QQ2564874169.RelationalSql
{
    public class SqlserverDbExecute : DbExecute
    {
        public SqlserverDbExecute(ISql sql) : base(sql)
        {
        }

        protected override ExecuteParam OnCreateSql(DbExecuteOperation operation, Type tableType, object model)
        {
            switch (operation)
            {
                case DbExecuteOperation.Delete:
                    return OnDelete(tableType, model);
                case DbExecuteOperation.Insert:
                    return OnInsert(tableType, model);
                case DbExecuteOperation.SetNull:
                case DbExecuteOperation.Update:
                    var e = (DbUpdateEventArgs) model;
                    return e.IsSetNull
                        ? OnSetNull(tableType, e.Sets, e.Where)
                        : OnUpdate(tableType, e.Sets, e.Where);
                case DbExecuteOperation.Proc:
                    return OnProc(tableType, model);
                default:
                    throw new NotSupportedException(operation.ToString());
            }
        }

        private static ExecuteParam OnInsert(Type tableType, object model)
        {
            var pvs = ParseModel(model, tableType);
            if (pvs.Count < 1)
                throw new ArgumentException("解析SQL失败，没有可新增的内容。");
            var left = string.Join(",", pvs.Keys.Select(i => $"[{i}]"));
            var right = string.Join(",", pvs.Keys.Select(i => $"@{i}"));

            var param = new ExecuteParam
            {
                Command = $"INSERT INTO [{tableType.Name}]({left}) VALUES({right})",
            };
            param.Parameters.Add(pvs);
            return param;
        }

        private static ExecuteParam OnDelete(Type tableType, object model)
        {
            var pvs = ParseModel(model, tableType);
            var where = string.Join(" AND ", pvs.Keys.Select(k => $"[{k}]=@{k}"));
            if (pvs.Count > 0)
                where = " WHERE " + where;
            var param = new ExecuteParam
            {
                Command = $"DELETE FROM [{tableType.Name}]{where}",
            };
            param.Parameters.Add(pvs);
            return param;
        }

        private static ExecuteParam OnUpdate(Type tableType, object setModel, object whereModel)
        {
            var nvs = ParseModel(setModel, tableType);
            if (nvs.Count < 1)
                throw new ArgumentException("解析SQL失败，没有可更新的内容。");
            var map = new Dictionary<string, object>();

            var sets = string.Join(",", nvs.Keys.Select(k => $"[{k}]=@s_{k}"));
            foreach (var key in nvs.Keys)
            {
                map.Add("s_" + key, nvs[key]);
            }

            var wvs = ParseModel(whereModel, tableType);
            var where = string.Join(" AND ", wvs.Keys.Select(k => $"[{k}]=@w_{k}"));
            if (wvs.Count > 0)
            {
                where = " WHERE " + where;
                foreach (var key in wvs.Keys)
                {
                    map.Add("w_" + key, wvs[key]);
                }
            }
            var param = new ExecuteParam
            {
                Command = $"UPDATE [{tableType.Name}] SET {sets}{where}"
            };
            param.Parameters.Add(map);
            return param;
        }

        private static ExecuteParam OnSetNull(Type tableType, object setModel, object whereModel)
        {
            var nvs = ParseModel(setModel, tableType);
            if (nvs.Count < 1)
                throw new ArgumentException("解析SQL失败，没有可更新的内容。");

            var sets = string.Join(",", nvs.Keys.Select(k => $"[{k}]=NULL"));
            var wvs = ParseModel(whereModel, tableType);
            var where = string.Join(" AND ", wvs.Keys.Select(k => $"[{k}]=@{k}"));
            if (wvs.Count > 0)
                where = " WHERE " + where;
            var param = new ExecuteParam
            {
                Command = $"UPDATE [{tableType.Name}] SET {sets}{where}"
            };
            param.Parameters.Add(wvs);
            return param;
        }

        private static ExecuteParam OnProc(Type tableType, object model)
        {
            var param = new ExecuteParam
            {
                Command = tableType.Name,
                IsProc = true
            };
            param.Parameters.Add(ParseModel(model));
            return param;
        }
    }
}
